package com.example.elm_server_api.filter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author 张建平
 * @createtime 2022/7/27 上午11:01
 */
@Slf4j
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    //token secret
    private String tokenSecret;

    //认证管理器
    private AuthenticationManager authenticationManager;

    public CustomAuthenticationFilter(AuthenticationManager authenticationManager, String tokenSecret) {
        this.authenticationManager = authenticationManager;
        this.tokenSecret = tokenSecret;
    }

    /**
     * 尝试认证
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        // return super.attemptAuthentication(request, response);

        // String username = obtainUsername(request); // 默认请求参数名是: username 和 password
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        log.info("Username is: {}, Password is: {}", username, password);
        //未认证前的 token，UsernamePasswordAuthenticationToken 是 Authentication 的实现类
        UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(username, password);

        // 这里安全框架会去调用 UserDetailsService 中的loadUserByUsername(String username)
        return authenticationManager.authenticate(token);
    }

    /**
     * 认证成功时发送JWT
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult)
            throws IOException, ServletException {

        User user = (User) authResult.getPrincipal();

        // 签名算法
        Algorithm algorithm = Algorithm.HMAC256(tokenSecret);

        //生成JWT Token(令牌)
        String access_token = JWT.create()
                                //主体
                                .withSubject(user.getUsername())
                                //令牌过期时间30 分钟
                                .withExpiresAt(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
                                //令牌发行者
                                .withIssuer(request.getRequestURL().toString())
                                //权限
                                .withClaim("roles",
                                        user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                                // .withClaim("abc", "")
                                //签名
                                .sign(algorithm);

        log.info("Generate JWT Token: {}", access_token);

        Map<String, String> tokens = new HashMap<>();
        tokens.put("access_token", access_token);

        response.setContentType("application/json; charset=UTF-8");

        //设置响应的标头字段: Authorization
        response.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + access_token);

        //生成JSON 写回客户端
        ObjectMapper objectMapper = new ObjectMapper();
        //响应是 Body
        objectMapper.writeValue(response.getOutputStream(), tokens);
    }

    /**
     * 认证不成功时
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    // @Override
    // protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
    //     super.unsuccessfulAuthentication(request, response, failed);
    // }
}
